/*
 * Copyright (C) 2013 The i18nCTS Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.i18nCTS.provider;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.xmlpull.v1.XmlSerializer;

import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.util.Xml;
import android.widget.Toast;

import com.googlecode.i18nCTS.R;
import com.googlecode.i18nCTS.provider.i18nCTSDatabase.i18nCTSDataSet;
import com.googlecode.i18nCTS.provider.i18nCTSDatabase.i18nCTSDataSet.i18nCTSData;
import com.googlecode.i18nCTS.util.XmlParser;
import com.googlecode.i18nCTS.view.CalendarSectionFragment;
import com.googlecode.i18nCTS.view.DateFormatSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsDayWeekdaySectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsNoonMidnightSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsRangeDiffMonthDiffYearSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsRangeDiffMonthSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsRangeDiffTimeSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsRangeSameMonthDiffYearSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsRangeSameMonthSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsSingleDateSectionFragment;
import com.googlecode.i18nCTS.view.DateUtilsWeekdayTimeSectionFragment;
import com.googlecode.i18nCTS.view.NumberSectionFragment;
import com.googlecode.i18nCTS.view.StringSectionFragment;

public class i18nCTSProvider { // this class should extend ContentProvider 

	private static final String TAG = "I18nCTS_" + i18nCTSProvider.class.getSimpleName();
	private i18nCTSDatabase mi18nCTSDatabase;
	private static Integer mCounter = 1;
	private static Integer mFailuerCounter = 0;
	private Context mContext = null;
	
	// ===== private methods =====
    private static String formatRowForExport(XmlSerializer serializer, String number, String misc, String result, String expected) {	
		try {
			boolean bFailed = !result.isEmpty() && !result.equals(expected);
			String debugInfo = "";
			
			// build Row
			serializer.startTag(null, "Row");
			// if not same, set row color = RED
			if (bFailed){
				serializer.attribute(null, "ss:StyleID", "s21");
				mFailuerCounter++;
			}
			
	        serializer.startTag(null, "Cell");
	        serializer.startTag(null, "Data");
	        serializer.attribute(null, "ss:Type", "String");
	        serializer.text(number);
	        serializer.endTag(null, "Data");
	        serializer.endTag(null, "Cell");

	        serializer.startTag(null, "Cell");
	        serializer.startTag(null, "Data");
	        serializer.attribute(null, "ss:Type", "String");
	        serializer.text(result);
	        serializer.endTag(null, "Data");

	        serializer.startTag(null, "Comment");
	        serializer.startTag(null, "Data");
	        serializer.text(expected);
	        serializer.endTag(null, "Data");	        
	        serializer.endTag(null, "Comment");
	        
	        serializer.endTag(null, "Cell");

	        serializer.startTag(null, "Cell");
	        serializer.startTag(null, "Data");
	        serializer.attribute(null, "ss:Type", "String");
	        serializer.text(misc);
	        serializer.endTag(null, "Data");
	        
			// add more data for debugging
	        if (bFailed){
	        	debugInfo = "result: [";
	        	for (int i = 0; i < result.length(); i++) {
	        		debugInfo += result.codePointAt(i) + " ";
	        	}
	        	debugInfo += "]\r\nexpected: [";
	        	for (int i = 0; i < expected.length(); i++) {
	        		debugInfo += expected.codePointAt(i) + " ";
	        	}
	        	debugInfo += "]";
	        	
		        serializer.startTag(null, "Comment");
		        serializer.startTag(null, "Data");
		        serializer.text(debugInfo);
		        serializer.endTag(null, "Data");	        
		        serializer.endTag(null, "Comment");
			}
	        serializer.endTag(null, "Cell");

	        serializer.endTag(null,"Row");
	    }catch(Exception e)
	    {
	        Log.e("Exception","Exception occured in writing");
	    }
		return number + result;
	}
    
    private static void formatRowForImport(XmlSerializer serializer, String result) {	
		try {
			// build Row
			serializer.startTag(null, "Row");

	        serializer.startTag(null, "Cell");
	        serializer.startTag(null, "Data");
	        serializer.attribute(null, "ss:Type", "String");
	        serializer.text(result);
	        serializer.endTag(null, "Data");	        
	        serializer.endTag(null, "Cell");
	        
	        serializer.endTag(null,"Row");
	    }catch(Exception e)
	    {
	        Log.e("Exception","Exception occured in writing");
	    }
	}
	
	private String addHeaderRow(String str) {
		return "<thead> <tr> <th COLSPAN=3 align=\"left\">" + str + "</th> </tr> </thead>";
	}
	
	private String addFooterRow(Integer failed, Integer total) {
		String totalFailed = failed.toString() + "/" + total.toString() + " (" + (failed*100/total) + "%)";
	    return 	"<tfoot> <tr> <td COLSPAN=2>Total Failed</td> <td>" + totalFailed + "</td> </tr> </tfoot>";
	}
	
	private String addRow(String result, String expected, String misc) throws UnsupportedEncodingException {	
		Boolean failed = (!result.equals(expected));
		
		if (failed) 
			mFailuerCounter++;
		
		String TableRowColor = (failed) ? "<tr bgcolor=\"red\">": "<tr>";
		return TableRowColor +  "<td>#" + mCounter++ + "</td>" +  "<td>" + result + "</td>" +  "<td width=\"100%\">" + misc + "</td>" +"</tr>";
	}

    private Boolean getExpectedList(String category, List<String> expectedList) {
        // write out the result to a file
        String defaultLocaleFileName = "in_" + category + "_" + Locale.getDefault().toString() + ".xml";
        FileInputStream fileis = null;
        try{
        	fileis = mContext.openFileInput(defaultLocaleFileName);
        } catch(FileNotFoundException e)
        {
        	CharSequence text = "Input file not found.  [/data/data/com.googlecode.i18nCTS/file/" + defaultLocaleFileName + "]";
        	int duration = Toast.LENGTH_LONG;
        	Toast toast = Toast.makeText(mContext, text, duration);
        	toast.show();
        	
            Log.e("InputFileNotFoundException",e.toString());
            return false;
        }
        
        try{
            XmlParser xmlParser = new XmlParser();
            List<XmlParser.Entry> entries = null;
            entries = xmlParser.parse(fileis);

            for (XmlParser.Entry entry : entries) {
            	expectedList.add(entry.mCell);
            }
            
            fileis.close();
            return true;
        }catch(Exception e)
        {
            Log.e("Exception","Exception occured in writing");
        }
        
        return false;
    }
    
    private void createAllImportFile(String category, List<String> listExpected) {
    	// if no data then return 
    	if (listExpected.size() == 0) {
    		Log.d("FileProvider", "createImportFile: listExpected.size() == 0");
    		return;
    	}
    	
        // write out the result to a file
        String defaultLocaleFileName = "in_" + category + "_" + Locale.getDefault().toString() + ".xml";
        FileOutputStream fileos = null;
        try{
            fileos = mContext.openFileOutput(defaultLocaleFileName, Context.MODE_PRIVATE);
        } catch(FileNotFoundException e)
        {
            Log.e("FileNotFoundException",e.toString());
        }
        
        XmlSerializer serializer = Xml.newSerializer();
        try{
	        serializer.setOutput(fileos, "UTF-8");
	        serializer.startDocument(null, Boolean.valueOf(true));
	        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
	        serializer.startTag(null, "Workbook");
	        serializer.attribute(null, "xmlns", "urn:schemas-microsoft-com:office:spreadsheet");
	        serializer.attribute(null, "xmlns:o", "urn:schemas-microsoft-com:office:office");
	        serializer.attribute(null, "xmlns:x", "urn:schemas-microsoft-com:office:excel");
	        serializer.attribute(null, "xmlns:ss", "urn:schemas-microsoft-com:office:spreadsheet");
	        serializer.attribute(null, "xmlns:html", "http://www.w3.org/TR/REC-html40");
	        	        
	        serializer.startTag(null, "Worksheet");
	        serializer.attribute(null, "ss:Name", Locale.getDefault().toString());
	        
	        serializer.startTag(null, "Table");	        
	        
			for (String list : listExpected) {
				formatRowForImport(serializer, list);
			}
			
	        serializer.endTag(null,"Table");
	        serializer.endTag(null,"Worksheet");
	        serializer.endTag(null,"Workbook");
	        serializer.endDocument();
	        serializer.flush();
	        fileos.close();
        }catch(Exception e)
        {
            Log.e("Exception","Exception occured in writing");
        }
    }

    // ===== public methods =====
    public i18nCTSProvider(Context context) {
    	mContext = context;
    	mi18nCTSDatabase = new i18nCTSDatabase();
	    mCounter = 1;
	}
    
	public boolean importAllXMLFile() {
		int index = 0;
		
		// import RunAll master if exist
        List<String> expectedAllList = new ArrayList<String>();

        if (getExpectedList("RunAll", expectedAllList)) {	
            List<String> expectedList = new ArrayList<String>();
        	
			// ******************* Verison 1.0 *******************
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty() || (expectedAllList.get(i).equals("royyokoyama"))) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsSingleDateSectionFragment.CATEGORY, expectedList);
        	expectedList.clear(); 
			
        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsRangeDiffTimeSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();
        	
        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsRangeSameMonthSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();
            
        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsRangeDiffMonthSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();
        	
        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsRangeSameMonthDiffYearSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();
       	
        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsRangeDiffMonthDiffYearSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();

        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsNoonMidnightSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();

        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateFormatSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();

        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(NumberSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();
        	
        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(CalendarSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();

        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(StringSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();

			// ******************* End of verison 1.0 *******************
			
			// Start of verison 1.1
        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsDayWeekdaySectionFragment.CATEGORY, expectedList);
        	expectedList.clear();

        	index++;
            for (int i = index; i < expectedAllList.size(); i++, index++) {
            	if (expectedAllList.get(i).isEmpty()) 
            		break;
            	expectedList.add(expectedAllList.get(i).trim());
            }            
        	createAllImportFile(DateUtilsWeekdayTimeSectionFragment.CATEGORY, expectedList);
        	expectedList.clear();
		}
        
		return true; 
	}
        
    public void fillExpected(String category) {
        List<String> expectedList = new ArrayList<String>();

        if (getExpectedList(category, expectedList)) {		
			mCounter = 0; // zero index array List
			i18nCTSDataSet data = mi18nCTSDatabase.getDataSet();
			for (i18nCTSData row : data.getData()) {
				if (!row.getResult().isEmpty() && row.getExpected().isEmpty() && (mCounter < expectedList.size())) {
					row.setExpected(expectedList.get(mCounter++));
				}
			}
		}
    }

	public void importXMLFile(String category) {
        List<String> expectedList = new ArrayList<String>();

		if (getExpectedList(category, expectedList)) {		
			mCounter = 0; // zero index array List
			for (i18nCTSDataSet dataset : mi18nCTSDatabase.getTable()) {
				if (dataset.getData() != null) {
					for (i18nCTSData row : dataset.getData()) {
						if (!row.getResult().isEmpty() && (mCounter < expectedList.size())) {
							row.setExpected(expectedList.get(mCounter++));
						}
					}
				}
			}
		}
	}

	public void exportXMLFile(String category) {
		mCounter = 1;

        // write out the result to a file
        String defaultLocaleFileName = category + "_" + Locale.getDefault().toString() + ".xml";
        String buildNo = Build.DISPLAY;
        FileOutputStream fileos = null;
        try{
            fileos = mContext.openFileOutput(defaultLocaleFileName, Context.MODE_PRIVATE);
        } catch(FileNotFoundException e)
        {
        	Toast toast = Toast.makeText(mContext, "Error creating result file [" + defaultLocaleFileName + "] " + e.toString(), Toast.LENGTH_SHORT);
        	toast.show();

        	Log.e("FileNotFoundException",e.toString());
        }

        mFailuerCounter = 0;

        XmlSerializer serializer = Xml.newSerializer();
        try{
	        serializer.setOutput(fileos, "UTF-8");
	        serializer.startDocument(null, Boolean.valueOf(true));
	        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
	        serializer.startTag(null, "Workbook");
	        serializer.attribute(null, "xmlns", "urn:schemas-microsoft-com:office:spreadsheet");
	        serializer.attribute(null, "xmlns:o", "urn:schemas-microsoft-com:office:office");
	        serializer.attribute(null, "xmlns:x", "urn:schemas-microsoft-com:office:excel");
	        serializer.attribute(null, "xmlns:ss", "urn:schemas-microsoft-com:office:spreadsheet");
	        serializer.attribute(null, "xmlns:html", "http://www.w3.org/TR/REC-html40");
	        
	        // specify color
	        serializer.startTag(null, "Styles");
	        serializer.startTag(null, "Style");
	        serializer.attribute(null, "ss:ID", "s21");
	        serializer.startTag(null, "Interior");
	        serializer.attribute(null, "ss:Color", "#DD0806");
	        serializer.attribute(null, "ss:Pattern", "Solid");
	        serializer.endTag(null, "Interior");
	        serializer.endTag(null, "Style");
	        serializer.endTag(null, "Styles");

	        
	        serializer.startTag(null, "Worksheet");
	        serializer.attribute(null, "ss:Name", Locale.getDefault().toString());
	        
	        serializer.startTag(null, "Table");	        
	        // Row 1: BUILD NO
	        serializer.startTag(null, "Row");
	        
	        serializer.startTag(null, "Cell");
	        serializer.startTag(null, "Data");
	        serializer.attribute(null, "ss:Type", "String");
	        serializer.text(buildNo);
	        serializer.endTag(null, "Data");
	        serializer.endTag(null, "Cell");

	        serializer.endTag(null,"Row");
	        
			for (i18nCTSDataSet dataset : mi18nCTSDatabase.getTable()) {
				// first row.  Header
				formatRowForExport(serializer, dataset.getSubTitle(), "", "", "");

				// second row.  results
				if (dataset.getData() != null) {
					for (i18nCTSData row : dataset.getData()) {
						formatRowForExport(serializer, "#"+mCounter+" ", row.getMisc(), row.getResult(), row.getExpected());
						mCounter++;
					}		
				}
			}
			
	        // Last Row : Total Failed
	        serializer.startTag(null, "Row");
	        
	        serializer.startTag(null, "Cell");
	        serializer.startTag(null, "Data");
	        serializer.attribute(null, "ss:Type", "String");
	        serializer.text("Total Failed");
	        serializer.endTag(null, "Data");
	        serializer.endTag(null, "Cell");

	        serializer.startTag(null, "Cell");
	        serializer.startTag(null, "Data");
	        serializer.attribute(null, "ss:Type", "String");
	        mCounter--;
			String totalFailed = mFailuerCounter.toString() + "/" + mCounter.toString() + " (" + (mFailuerCounter*100/mCounter) + "%)"; 
	        serializer.text(totalFailed);
	        serializer.endTag(null, "Data");
	        serializer.endTag(null, "Cell");

	        serializer.endTag(null,"Row");


	        serializer.endTag(null,"Table");
	        serializer.endTag(null,"Worksheet");
	        serializer.endTag(null,"Workbook");
	        serializer.endDocument();
	        serializer.flush();
	        fileos.close();
        }catch(Exception e)
        {
            Log.e(TAG, mContext.getString(R.string.exception_writing));
        }
	}

	public String getHTML() {
		String rtnStr = null;
	
		mCounter = 1;
		mFailuerCounter = 0;
		rtnStr = "<html> <head> <meta charset=\"UTF-8\"> </head><body>";
		rtnStr += "<table border=\"1\">";

		for (i18nCTSDataSet dataset : mi18nCTSDatabase.getTable()) {
			// first row.  Header
			rtnStr += addHeaderRow(dataset.getSubTitle());

			// second row.  results
			if (dataset.getData() != null) {
				for (i18nCTSData row : dataset.getData()) {
					try {
						rtnStr += addRow(row.getResult(), row.getExpected(), row.getMisc());
					} catch (UnsupportedEncodingException e) {
						e.printStackTrace();
					}
				}		
			}
		}
		
		// count result
		rtnStr += addFooterRow(mFailuerCounter, mCounter-1);
		
		rtnStr += "</table>";
		rtnStr += "</body></html>";
		
		return rtnStr;
	}
	
	public void addData(String result, String misc) {
		mi18nCTSDatabase.addData(result, misc);
	}
	
	public void addTitle(String category) {
		String subtitle = category +": " + Locale.getDefault().getDisplayName() + "\u200f(" + Locale.getDefault().toString() + ")";
		mi18nCTSDatabase.addTitle(subtitle);
	}
	
	public void commit() {
		mi18nCTSDatabase.commit();
	}
}
